package com.gzx.plugin.tjzy.modular.call.predict.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.gzx.plugin.tjzy.modular.caffeine.UserCaffeine;
import com.gzx.plugin.tjzy.modular.call.predict.bo.GzxPredictCallAddBo;
import com.gzx.plugin.tjzy.modular.call.predict.bo.GzxPredictCallExcelBo;
import com.gzx.plugin.tjzy.modular.call.predict.consts.GzxPredictCallConstant;
import com.gzx.plugin.tjzy.modular.call.predict.consts.GzxPredictCallTypeConstant;
import com.gzx.plugin.tjzy.modular.call.predict.entity.GzxPredictCall;
import com.gzx.plugin.tjzy.modular.call.predict.entity.GzxPredictCallDetail;
import com.gzx.plugin.tjzy.modular.call.predict.service.GzxPredictCallDbService;
import com.gzx.plugin.tjzy.modular.call.predict.service.GzxPredictCallImportService;
import com.gzx.plugin.tjzy.modular.call.receipt.entity.GzxHtCallReceiptBusiness;
import com.gzx.plugin.tjzy.modular.call.receipt.mapper.GzxHtCallReceiptBusinessMapper;
import com.gzx.plugin.tjzy.modular.gzxcase.entity.CaseQueryParam;
import com.gzx.plugin.tjzy.modular.gzxcase.entity.GzxCase;
import com.gzx.plugin.tjzy.modular.gzxcase.entity.GzxCaseDetail;
import com.gzx.plugin.tjzy.modular.gzxcase.service.GzxCaseDbService;
import com.gzx.plugin.tjzy.modular.liaison.entity.GzxDebtorLiaison;
import com.gzx.plugin.tjzy.modular.liaison.service.GzxDebtorLiaisonService;
import com.gzx.plugin.tjzy.modular.phone.error.cache.GzxErrorPhoneCache;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
import vip.xiaonuo.common.consts.BatchNumberConstant;
import vip.xiaonuo.common.pojo.CommonResult;
import vip.xiaonuo.common.util.*;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author zjt
 * @description
 * @date 2025/6/9 17:08
 */
@Service
@RequiredArgsConstructor
@DS("tjzy-async")
public class GzxPredictCallImportServiceImpl implements GzxPredictCallImportService {

    private final GzxPredictCallDbService predictCallDbService;

    private final GzxCaseDbService caseDbService;

    private final GzxDebtorLiaisonService debtorLiaisonService;

    private final GzxHtCallReceiptBusinessMapper htCallReceiptBusinessMapper;

    private final UserCaffeine userCaffeine;

    private final GzxErrorPhoneCache errorPhoneCache;

    @Value("${pre.out.count}")
    private Integer preOutCallCount;

    /**
     * 预测外呼数据导入
     *
     * @param multipartFile
     * @param predictCallAddBo
     * @return
     */
    @Override
    @DSTransactional(rollbackFor = Exception.class)
    public CommonResult<String> importExcel(MultipartFile multipartFile, GzxPredictCallAddBo predictCallAddBo) {
        //解析Excel
        List<GzxPredictCallExcelBo> predictCallExcelBoList = CommonExcelUtil.importExcel(multipartFile, GzxPredictCallExcelBo.class);
        if (CollectionUtil.isEmpty(predictCallExcelBoList)) {
            return CommonResult.error("数据导入失败，Excel数据为空");
        }
        if (predictCallAddBo.getIsPhoneAes() == 1) {
            predictCallExcelBoList.forEach(bo -> bo.setLiaisonPhone(CommonCryptogramUtil.doSm1AesDecrypt(bo.getLiaisonPhone())));
        }
        StringBuilder sb = new StringBuilder("Excel总计").append(predictCallExcelBoList.size()).append("条\n");
        sb.append(startSavePredictCallProcess(predictCallAddBo, predictCallExcelBoList));
        return CommonResult.data(sb.toString());
    }

    /**
     * 预测外呼复制
     *
     * @param predictCallAddBo
     * @return
     */
    @Override
    @DSTransactional(rollbackFor = Exception.class)
    public CommonResult<String> copyPredictCallDetail(GzxPredictCallAddBo predictCallAddBo) {
        //根据批次号查询主表数据和明细数据
        GzxPredictCall predictCall = predictCallDbService.selectOne(Wrappers.<GzxPredictCall>lambdaQuery()
                .eq(GzxPredictCall::getBatchNumber, predictCallAddBo.getBatchNumber()));
        predictCallAddBo.setIsPhoneAes(0);
        predictCallAddBo.setCallType(predictCall.getCallType());
        predictCallAddBo.setImportType(GzxPredictCallConstant.MEDIATOR_IMPORT);
        predictCallAddBo.setQueueCode(predictCall.getQueueCode());
        //查询明细数据
        List<GzxPredictCallDetail> predictCallDetailList = predictCallDbService.selectDetailList(Wrappers.<GzxPredictCallDetail>lambdaQuery()
                .eq(GzxPredictCallDetail::getBatchNumber, predictCallAddBo.getBatchNumber()));
        //过滤掉已接听、已拨打
        if (predictCallAddBo.getFilterType() == 1 || predictCallAddBo.getFilterType() == 2) {
            //查询话单数据
            List<GzxHtCallReceiptBusiness> htCallReceiptBusinessList = htCallReceiptBusinessMapper.selectList(Wrappers.<GzxHtCallReceiptBusiness>lambdaQuery()
                    .select(GzxHtCallReceiptBusiness::getCallee)
                    .eq(GzxHtCallReceiptBusiness::getTaskId, predictCall.getTaskId())
                    //过滤掉已接听
                    .gt(predictCallAddBo.getFilterType() == 1, GzxHtCallReceiptBusiness::getCallDuration, 0));
            List<String> phoneList = htCallReceiptBusinessList.stream().map(GzxHtCallReceiptBusiness::getCallee).toList();
            List<GzxPredictCallDetail> resultList = new ArrayList<>();
            for (GzxPredictCallDetail predictCallDetail : predictCallDetailList) {
                if (!phoneList.contains(predictCallDetail.getLiaisonPhone())) {
                    resultList.add(predictCallDetail);
                }
            }
            predictCallDetailList = resultList;
        }
        List<GzxPredictCallExcelBo> predictCallExcelBoList = MapstructUtils.convert(predictCallDetailList, GzxPredictCallExcelBo.class);
        return CommonResult.data(startSavePredictCallProcess(predictCallAddBo, predictCallExcelBoList));
    }

    //开始预测外呼数据导入流程
    private String startSavePredictCallProcess(GzxPredictCallAddBo predictCallAddBo, List<GzxPredictCallExcelBo> predictCallExcelBoList) {
        StringBuilder errMsg = new StringBuilder();
        //根据手机号去重
        List<GzxPredictCallExcelBo> distinctList = distinctByPhone(predictCallExcelBoList);
        Map<String, JSONObject> userMap = userCaffeine.getUserMap();
        if (GzxPredictCallConstant.MEDIATOR_IMPORT.equals(predictCallAddBo.getImportType())) {
            distinctList = distinctList.stream().peek(item -> item.setMediatorName(StpLoginUserUtil.getLoginUser().getAccount())).toList();
        }
        //过滤手机号和投诉用户和异常数据
        List<GzxPredictCallExcelBo> resultList = filterPhone(distinctList, predictCallAddBo.getIsPhoneAes());
        //根据调解员分组
        Map<String, List<GzxPredictCallExcelBo>> resultMapList = resultList.stream().collect(Collectors.groupingBy(GzxPredictCallExcelBo::getMediatorName));
        int successCount = 0;
        for (Map.Entry<String, List<GzxPredictCallExcelBo>> entry : resultMapList.entrySet()) {
            if (CollectionUtil.isNotEmpty(entry.getValue())) {
                int totalCount = entry.getValue().size();
                if (userMap.containsKey(entry.getKey())) {
                    if (preOutCallCount < totalCount) {
                        errMsg.append(entry.getKey()).append("数据量大于").append(preOutCallCount).append("条，请分批次导入\n");
                        continue;
                    }
                    JSONObject user = userMap.get(entry.getKey());
                    complementCaseId(entry.getValue(), user.getStr("id"));
                    //生成批次号
                    String batchNumber = generateBatchBatchNumber(predictCallAddBo.getCallType());
                    //构建主表数据
                    predictCallAddBo.setBatchNumber(batchNumber);
                    //保存批次表
                    predictCallDbService.saveBatch(predictCallAddBo, user, totalCount);
                    //保存明细表
                    predictCallDbService.saveDetail(predictCallAddBo, entry.getValue(), user);
                    successCount += totalCount;
                } else {
                    errMsg.append(entry.getKey()).append("：不存在，已忽略").append(totalCount).append("条\n");
                }
            }
        }
        return errMsg.append("成功").append(successCount).append("条").toString();
    }

    private List<GzxPredictCallExcelBo> distinctByPhone(List<GzxPredictCallExcelBo> list) {
        return list.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.toMap(
                                GzxPredictCallExcelBo::getLiaisonPhone,
                                Function.identity(),
                                (existing, replacement) -> existing),
                        map -> new ArrayList<>(map.values())));
    }

    private List<GzxPredictCallExcelBo> filterPhone(List<GzxPredictCallExcelBo> list, Integer isAes) {
        List<GzxPredictCallExcelBo> resultList = new ArrayList<>();
        List<String> filterPhoneList = errorPhoneCache.getPhoneFilterCache();
        Set<String> complaintUserList = errorPhoneCache.getComplaintUserCache().keySet();
        for (GzxPredictCallExcelBo excelBo : list) {
            String liaisonPhone = isAes == 1 ? CommonCryptogramUtil.doAesCFBDecrypt(excelBo.getLiaisonPhone()) : excelBo.getLiaisonPhone();
            if ((StringUtils.isBlank(excelBo.getLiaisonName()) || excelBo.getLiaisonName().length() < 50)
                    && (StringUtils.isBlank(excelBo.getCustomerInfo()) || excelBo.getCustomerInfo().length() < 100)
                    && StringUtils.isNotBlank(liaisonPhone)
                    && CommonStringFormatUtil.isValidPhone(liaisonPhone)
                    && !filterPhoneList.contains(liaisonPhone) && !complaintUserList.contains(liaisonPhone)) {
                excelBo.setLiaisonPhone(liaisonPhone);
                resultList.add(excelBo);
            }
        }
        return resultList;
    }

    private String generateBatchBatchNumber(Integer callType) {
        String suffix = null;
        if (Objects.equals(callType, GzxPredictCallTypeConstant.SINGLE_CALL)) {
            suffix = "SP";
        }
        if (Objects.equals(callType, GzxPredictCallTypeConstant.MULTI_CALL)) {
            suffix = "MP";
        }
        return suffix + CommonBatchNumberUtil.getBatchNumber(BatchNumberConstant.PRE_BATCH_NUMBER);
    }

    private void complementCaseId(List<GzxPredictCallExcelBo> predictCallExcelBoList, String userId) {
        //优先根据甲方系统主键匹配案件
        Set<String> parentBusinessIdSet = predictCallExcelBoList.stream().map(GzxPredictCallExcelBo::getPartnerBusinessId).filter(Objects::nonNull).filter(id -> !id.isEmpty()).collect(Collectors.toSet());
        if (CollectionUtil.isNotEmpty(parentBusinessIdSet)) {
            //根据甲方系统主键查询案件详情
            List<GzxCaseDetail> caseDetailList = caseDbService.selectCaseDetailList(Wrappers.<GzxCaseDetail>lambdaQuery()
                    .select(GzxCaseDetail::getBatchNumber, GzxCaseDetail::getDebtorCardId, GzxCaseDetail::getPartnerBusinessId)
                    .in(GzxCaseDetail::getPartnerBusinessId, parentBusinessIdSet));
            //批量查询案件
            Map<String, Set<String>> batchEncryptedDebtorCardIds = caseDetailList.stream()
                    .collect(Collectors.groupingBy(
                            GzxCaseDetail::getBatchNumber,
                            Collectors.mapping(
                                    caseDetail -> CommonCryptogramUtil.doSm1AesEncrypt(caseDetail.getDebtorCardId()),
                                    Collectors.toSet())));
            if (CollectionUtil.isNotEmpty(batchEncryptedDebtorCardIds)) {
                List<GzxCase> caseList = caseDbService.listCasesByBatchAndDebtorIds(
                        batchEncryptedDebtorCardIds.entrySet().stream()
                                .map(e -> new CaseQueryParam(e.getKey(), e.getValue()))
                                .toList());
                //案件转Map
                Map<String, Long> caseMap = caseList.stream()
                        .filter(caseObj -> StringUtils.isNotEmpty(caseObj.getDebtorCardId()))
                        .collect(Collectors.toMap(
                                GzxCase::getDebtorCardId,
                                GzxCase::getId,
                                Math::max,
                                HashMap::new
                        ));
                Set<String> validDebtorCardIds = caseMap.keySet();
                Map<String, Long> caseDetailMap = caseDetailList.stream()
                        .parallel()
                        .filter(detail ->
                                StringUtils.isNotEmpty(detail.getPartnerBusinessId()) &&
                                        StringUtils.isNotEmpty(CommonCryptogramUtil.doSm1AesEncrypt(detail.getDebtorCardId())) &&
                                        validDebtorCardIds.contains(CommonCryptogramUtil.doSm1AesEncrypt(detail.getDebtorCardId())))
                        .collect(Collectors.toMap(
                                GzxCaseDetail::getPartnerBusinessId,
                                detail -> caseMap.get(CommonCryptogramUtil.doSm1AesEncrypt(detail.getDebtorCardId())),
                                (existingId, newId) -> newId,
                                LinkedHashMap::new
                        ));
                predictCallExcelBoList.forEach(detail -> {
                    if (caseDetailMap.containsKey(detail.getPartnerBusinessId())) {
                        detail.setCaseId(caseDetailMap.get(detail.getPartnerBusinessId()));
                    }
                });
            }
        }
        //查询出CaseId为null，且手机号非空的数据
        List<String> liaisonPhoneList = predictCallExcelBoList.stream()
                .filter(d -> d.getCaseId() == null)
                .map(GzxPredictCallExcelBo::getLiaisonPhone)
                .filter(StringUtils::isNotEmpty)
                .distinct()
                .map(CommonCryptogramUtil::doSm1AesEncrypt)
                .toList();
        if (CollectionUtil.isNotEmpty(liaisonPhoneList)) {
            //根据手机号查询三方联系人
            List<GzxDebtorLiaison> debtorLiaisonList = debtorLiaisonService.selectList(Wrappers.<GzxDebtorLiaison>lambdaQuery().in(GzxDebtorLiaison::getLiaisonPhone, liaisonPhoneList));
            //加密身份证号
            Set<String> debtorCardIdSet = debtorLiaisonList.stream()
                    .map(GzxDebtorLiaison::getDebtorCardId)
                    .map(CommonCryptogramUtil::doSm1AesEncrypt)
                    .collect(Collectors.toSet());
            if (CollectionUtil.isNotEmpty(debtorCardIdSet)) {
                //根据身份证号查询案件，身份证号相同，保留最新的案件
                Map<String, Long> caseMap = caseDbService.selectCaseList(
                                Wrappers.<GzxCase>lambdaQuery()
                                        .in(GzxCase::getDebtorCardId, debtorCardIdSet)
                                        .eq(GzxCase::getMediatorId, userId))
                        .stream()
                        .filter(caseObj -> caseObj.getDebtorCardId() != null)
                        .collect(Collectors.toMap(
                                GzxCase::getDebtorCardId,
                                GzxCase::getId,
                                Math::max,
                                HashMap::new));
                //联系人信息转Map，手机号为Key，案件ID为Value
                Map<String, Long> liaisonMap = debtorLiaisonList.stream()
                        .filter(liaison -> caseMap.containsKey(liaison.getDebtorCardId()))
                        .collect(Collectors.toMap(
                                GzxDebtorLiaison::getLiaisonPhone,
                                liaison -> caseMap.get(liaison.getDebtorCardId()),
                                Math::max,
                                HashMap::new));
                //填充案件信息，根据手机号匹配案件
                predictCallExcelBoList.stream()
                        .filter(d -> d.getCaseId() == null)
                        .forEach(detail -> {
                            if (liaisonMap.containsKey(CommonCryptogramUtil.doSm1AesDecrypt(detail.getLiaisonPhone()))) {
                                detail.setCaseId(liaisonMap.get(CommonCryptogramUtil.doSm1AesDecrypt(detail.getLiaisonPhone())));
                            }
                        });
            }
        }
    }
}
